案例浅析:Discuz!
对 PostgreSQL 的数据管理层级建立了基本的了解,是未来工作的重要基础,但是现在我们所学所写,终究是“纸上得来终觉浅”,因此在这篇文章之中,我将选择一个更为切实的案例,做一个深入一些的讨论,进而更好地帮助读者们掌握这一方面的知识。
我们将把 Discuz! 作为我们所分析的对象,它是我国在 BBS 社区兴起时代应用最广泛的一款应用程序,支撑了许多优质的网站,曾为数以千万记的用户提供服务,尽管时过境迁, BBS 时代风云不再,但是它依旧是我们值得学习的对象。
Discuz! 对禁言记录的数据管理简介
很多社区之中,都有一些因为不友善行为而被关进“小黑屋”的用户,而在 Discuz! 中, pre_common_banned 数据表承担了这一部分的职责,参考如下:
/* 因为 Discuz! 原生应用于 MySQL 而不是 PostgreSQL,因此我对于代码做了一定的修订 */
DROP TABLE IF EXISTS pre_common_banned;
CREATE TABLE pre_common_banned (
id SERIAL NOT NULL, /* 用户的 ID */
ip CIDR NOT NULL DEFAULT '0.0.0.0', /* 用户的 IP */
/* 如果禁言的是 IP 段 */
lowerip CIDR NOT NULL DEFAULT '0.0.0.0',
upperip CIDR NOT NULL DEFAULT '0.0.0.0',
admin TEXT NOT NULL DEFAULT '',/* 操作的管理员 */
dateline INTEGER NOT NULL DEFAULT 0,/* 启动日期 */
expiration INTEGER NOT NULL DEFAULT 0,/* 持续时间 */
CHECK (expiration >= 0),
PRIMARY KEY (id)
);
可以发现,对于禁止发言这一操作,Discuz! 的设计实际上是在针对“禁言操作记录”在做管理(由此实现),而从实际的运行逻辑上面看,如下图所示:
言归正传,在这张表当中,我们可以发现,明显出现了不少之前我们所没有接触过的东西,在这里,将他们逐步解析出来,将会更好地帮助我们理解 SQL 这门编程语言。
- DROP TABLE 的 IF EXISTS 子句可以规避在数据表不存在时候的报错
SQL 语言的许多指令,实际上都有着很多配套的辅助指令(即子句)来帮助指导数据库的行为,IF EXISTS 就是其中的一个案例。
一般而言,在数据表不存在的时候,DROP TABLE 将会发出一个错误,进而终止数据管理操作的执行,而从实际场景上面看,这种反馈实际上非常不妥当(因为无论是数据表存在与否,这条指令的目标实际上就是要让这种数据 表不存在,所以对于要删除的表不存在时,跳过它们往往是一个更好的选择),而加入了 IF EXISTS 指令之后,DROP TABLE 的逻辑将会转变为“如果存在,即删除表;如果不存在,即跳过展开下一步工作”,进而达到了抓大放小,规避不重要错误的目的。 - 数据表属性的约束可以提升存储数据的正确性与质量
约束,本质上就是对于存储入库的数据的一种限制,一般由程序员根据业务实际需要进行配置。
比如在此处的 Discus! 中,使用NOT NULL
进而使得存储在表中对应数据记录属性的数据项非空,同时依托DEFAULT
为数据项配置默认数据(一般而言,在用户插入数据时,未指定的数据项将会被填写为 NULL,而当我们设定了默认之后,填写进入的数据将会是这些默认值(这就类似于默认头像))。
而 CHECK 则根据我们的逻辑表达式来对数据项目做检验,如此处,我们希望禁言记录的有效持续时间不是一个负数。 - CIDR 与 SERIAL 数据类型
CIDR 数据类型是 PostgreSQL 为处理网络 IP 数据而特意设计的数据类型,我们可以使用它存储 IP 段等网络方面的记录(本质:CIDR 是一种按照网络记录格式存储起来的字符串)。
SERIAL 数据类型则是 PostgreSQL 为存储序列化数字(即按照一定分布规律,如递增,递减而排列起来的数字)而设计的一种数据类型,我们可以使用它存储诸如排名,用户 ID 等信息。 - 主键
在 E.F.Codd 的文章之中,提出数据表中的各项数据应当按照灵活、便利的方式进行访问与查询,而“主键”(Primary Key)就被提出“用于标识关系表中某一条元组的某一项属性”,学术上的概念落地到工程实践上,即是数据库系统中非常重要的“索引”(Index),我们在表中将某条属性指定为“主键”以后,数据库系统将会围绕这项主键展开一定的工作(构建信息片段),进而促进未来我们在依据主键查询数据的时候,查询的性能可以有一个提升。
善于为数据表选择主键,构建索引是一门学问,因为不合适的主键不仅会浪费存储空间,同时反而会降低性能。
Discuz! 对用户收藏的数据管理简介
在社区之中,我们常常会保存自己感兴趣的文章,笔记等内容,而在 Discuz! 中,它们被集成到 pre_home_favorite 表中加以管理,参考如下的代码:
/* 根据 PostgreSQL 以及实际的需要进行了一定的修订 */
DROP TABLE IF EXISTS pre_home_favorite;
CREATE TABLE pre_home_favorite (
favid SERIAL NOT NULL, /* 收藏 ID */
uid INTEGER NOT NULL DEFAULT 0, /* 用户 ID */
id INTEGER NOT NULL DEFAULT 0, /* 本条记录 ID */
idtype TEXT NOT NULL DEFAULT '', /* ID类型 */
title TEXT NOT NULL DEFAULT '', /* 收藏标题 */
description TEXT NOT NULL, /* 收藏描述 */
dateline INTEGER NOT NULL DEFAULT 0, /* 收藏时间 */
PRIMARY KEY (favid)
);
可以看到,这张数据表遵循的是一个这样的管理结构:
而由此我们就可以很清晰的了解到各条数据所设立的意义,收藏记录按照用户 ID 与 用户所设立的收藏夹,分门别类地(idtype)被收纳在数据表中,而收藏的标题与描述及时间,则是我们所重点关注的内容,由此实现了对于收藏数据的管理。
在经历过这两则案例分析之后,对于 PostgreSQL 我们已经有了一个基本的认识与理解,在接下来的进程之中,我们将会就数据的进一步管理展开讨论,进而帮助你成为一名一流的 PostgreSQL 学习者。